<template>
  <view class="container">
    <view class="title">百度语音合成示例</view>
    
    <view class="input-area">
      <textarea v-model="inputText" placeholder="请输入要合成的文本"></textarea>
    </view>
    
    <view class="params-area">
      <view class="param-item">
        <text>发音人：</text>
        <picker @change="onVoiceChange" :value="voiceIndex" :range="voices" range-key="name">
          <view class="picker">{{voices[voiceIndex].name}}</view>
        </picker>
      </view>
      
      <view class="param-item">
        <text>语速({{ttsParams.spd}})：</text>
        <slider :value="ttsParams.spd" @change="onSpdChange" min="0" max="15" show-value />
      </view>
      
      <view class="param-item">
        <text>音量({{ttsParams.vol}})：</text>
        <slider :value="ttsParams.vol" @change="onVolChange" min="0" max="15" show-value />
      </view>
    </view>
    
    <view class="button-group">
      <button type="primary" @click="synthesizeSpeech">合成并播放</button>
      <button type="default" @click="pauseSpeech" :disabled="!audioControl">暂停</button>
      <button type="default" @click="resumeSpeech" :disabled="!audioControl">继续</button>
      <button type="warn" @click="destroySpeech" :disabled="!audioControl">停止并清理</button>
    </view>
  </view>
</template>

<script>
export default {
  data() {
    return {
      inputText: '您好，这是一段百度语音合成的测试文本。',
      audioControl: null,
      // 百度云配置信息
      config: {
        apiKey: 'cu3FeO72bJFKWQ3skyhYCxxv',
        secretKey: 'iYur0VU7Aph64YVBH4uJRsQrjwOorp32',
        cuid: '29f570d02c2749a1b3aca84fcf2be732', // 用户唯一标识，可以是设备ID
      },
      // TTS参数
      ttsParams: {
        spd: 5,  // 语速，取值0-15
        pit: 5,  // 音调，取值0-15
        vol: 7,  // 音量，取值0-15
        per: 0   // 发音人，默认度小美
      },
      // 发音人列表
      voices: [
        { id: 0, name: '度小美(女声)' },
        { id: 1, name: '度小宇(男声)' },
        { id: 3, name: '度逍遥(男声)' },
        { id: 4, name: '度丫丫(女童声)' },
        { id: 5, name: '度小娇(女声)' },
        { id: 103, name: '度米朵(可爱女声)' },
        { id: 106, name: '度博文(情感男声)' },
        { id: 110, name: '度小童(童声)' },
        { id: 111, name: '度小萌(萌妹子)' }
      ],
      voiceIndex: 0
    };
  },
  methods: {
    // 获取百度云access_token
    getBaiduToken() {
      return new Promise((resolve, reject) => {
        uni.request({
          url: 'https://openapi.baidu.com/oauth/2.0/token?grant_type=client_credentials&client_id=cu3FeO72bJFKWQ3skyhYCxxv&client_secret=iYur0VU7Aph64YVBH4uJRsQrjwOorp32',
          method: 'POST',
          success: (res) => {
            if (res.data && res.data.access_token) {
              resolve(res.data.access_token);
            } else {
              reject(new Error('获取token失败'));
            }
          },
          fail: (err) => {
            reject(err);
          }
        });
      });
    },
    
    // 语音合成并播放
    async textToSpeech(text, options = {}) {
      try {
        // 获取token
        const token = await this.getBaiduToken();
        console.log("token",token)
        // 合并参数
        const params = Object.assign({}, this.ttsParams, options);
        
        // 构建请求参数
        const postData = {
          tex: encodeURIComponent(text), // 文本内容需要编码
          tok: token,
          cuid: this.config.cuid,
          ctp: 1,
          lan: 'zh', // 中文
          spd: params.spd,
          pit: params.pit,
          vol: params.vol,
          per: params.per,
          aue: 3 // mp3格式
        };
        
        // 将参数转换为查询字符串
        const queryString = Object.keys(postData)
          .map(key => `${key}=${postData[key]}`)
          .join('&');
        
        // 显示加载提示
        uni.showLoading({ title: '生成语音中...' });
        
        // 下载语音文件
        const downloadRes = await new Promise((resolve, reject) => {
          uni.downloadFile({
            url: `https://tsn.baidu.com/text2audio?${queryString}`,
            success: (res) => {
              if (res.statusCode === 200) {
                resolve(res);
              } else {
                reject(new Error(`下载失败，状态码: ${res.statusCode}`));
              }
            },
            fail: (err) => {
              reject(err);
            }
          });
        });
        
        uni.hideLoading();
        
        // 检查是否成功下载
        if (downloadRes.tempFilePath) {
          // 创建音频实例
          const innerAudioContext = uni.createInnerAudioContext();
          innerAudioContext.src = downloadRes.tempFilePath;
          
          // 返回控制对象
          return {
            // 播放语音
            play: () => {
              innerAudioContext.play();
              return new Promise((resolve) => {
                innerAudioContext.onEnded(() => {
                  resolve();
                });
              });
            },
            // 暂停语音
            pause: () => {
              innerAudioContext.pause();
            },
            // 停止语音
            stop: () => {
              innerAudioContext.stop();
            },
            // 销毁并清理资源
            destroy: () => {
              innerAudioContext.destroy();
              // 删除临时文件
              uni.removeSavedFile({
                filePath: downloadRes.tempFilePath,
                complete: () => {
                  console.log('语音文件已删除');
                }
              });
            },
            // 获取音频文件路径
            getFilePath: () => {
              return downloadRes.tempFilePath;
            }
          };
        } else {
          throw new Error('语音合成失败，未返回有效的音频文件');
        }
      } catch (error) {
        uni.hideLoading();
        uni.showToast({
          title: '语音合成失败: ' + error.message,
          icon: 'none'
        });
        throw error;
      }
    },
    
    // 合成并播放按钮
    async synthesizeSpeech() {
      try {
        // 如果已有音频实例，先销毁
        if (this.audioControl) {
          this.audioControl.destroy();
          this.audioControl = null;
        }
        
        // 检查文本是否为空
        if (!this.inputText.trim()) {
          uni.showToast({
            title: '请输入需要合成的文本',
            icon: 'none'
          });
          return;
        }
        
        // 合成语音
        this.audioControl = await this.textToSpeech(this.inputText);
        
        // 播放语音
        await this.audioControl.play();
        console.log('播放完成');
      } catch (error) {
        console.error('语音合成失败:', error);
      }
    },
    
    // 暂停播放
    pauseSpeech() {
      if (this.audioControl) {
        this.audioControl.pause();
      } else {
        uni.showToast({
          title: '请先合成语音',
          icon: 'none'
        });
      }
    },
    
    // 继续播放
    resumeSpeech() {
      if (this.audioControl) {
        this.audioControl.play();
      } else {
        uni.showToast({
          title: '请先合成语音',
          icon: 'none'
        });
      }
    },
    
    // 停止播放并清理资源
    destroySpeech() {
      if (this.audioControl) {
        this.audioControl.destroy();
        this.audioControl = null;
        uni.showToast({
          title: '已停止播放并清理资源',
          icon: 'none'
        });
      }
    },
    
    // 发音人选择变化
    onVoiceChange(e) {
      const index = e.detail.value;
      this.voiceIndex = index;
      this.ttsParams.per = this.voices[index].id;
    },
    
    // 语速变化
    onSpdChange(e) {
      this.ttsParams.spd = e.detail.value;
    },
    
    // 音量变化
    onVolChange(e) {
      this.ttsParams.vol = e.detail.value;
    }
  },
  // 页面卸载时清理资源
  onUnload() {
    if (this.audioControl) {
      this.audioControl.destroy();
    }
  }
}
</script>

<style>
.container {
  padding: 20px;
}
.title {
  font-size: 18px;
  font-weight: bold;
  margin-bottom: 20px;
  text-align: center;
}
.input-area {
  margin-bottom: 20px;
}
.input-area textarea {
  width: 100%;
  height: 120px;
  border: 1px solid #ddd;
  border-radius: 5px;
  padding: 10px;
}
.params-area {
  margin-bottom: 20px;
  background-color: #f5f5f5;
  padding: 10px;
  border-radius: 5px;
}
.param-item {
  margin-bottom: 10px;
}
.picker {
  height: 40px;
  line-height: 40px;
  border: 1px solid #ddd;
  border-radius: 4px;
  padding: 0 10px;
  background-color: #fff;
}
.button-group {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
</style>
